#include "teststdafx.h"
#include "stdafx.h"
#include <gtest/gtest.h>

#include "Business/Funds.h"
#include "Business/ClassicArbitrageContracts.h"
#include "Business/Dealing.h"
#include "Service/MarketStorage.h"
#include "Service/FundsService.h"
#include "Business/Customer.h"

const double ORGINAL_AMOUNT = 10000.00;
const double FREEZE_AMOUNT = 1000.00;
const double OPEN_AMOUNT = 1000.00;
const double CHARGE_AMOUNT = 100.00;
const double PROFIT_AMOUNT = 90.00;

class FundsTest : public testing::Test
{
protected:

	virtual void SetUp()
	{
	}

	virtual void TearDown()
	{

	}
	Market const& CreateMarket0()
	{
		Variety variety("rb1010",1,10);
		Margins margins;
		margins.Add(Margin("test1",0.1));
		margins.Add(Margin("test2",0.2));
		variety.SetMargins(margins);
		Charge openExchageCharge("open exchange charge",Charge::PERCENT, 0.10);
		Charge openCompanyCharge("open company charge",Charge::PER_QUANTITY, 5);
		Charge offsetExchangeCharge("offset exchange charge",Charge::PER_QUANTITY, 10);
		Charge offsetCompanyCharge("offset company charge",Charge::PERCENT, 0.10);

		Charges openCharges;
		openCharges.push_back(openExchageCharge);
		openCharges.push_back(openCompanyCharge);
		Charges offsetCharges;
		offsetCharges.push_back(offsetExchangeCharge);
		offsetCharges.push_back(offsetCompanyCharge);

		variety.SetOperationCharges(Operation::OPEN, openCharges);
		variety.SetOperationCharges(Operation::OFFSET,offsetCharges);
		double lastPrice = 3000.00;
		Prices buyPrices;
		buyPrices[0] = Price(3010,7);
		Prices sellPrices;
		sellPrices[0] = Price(2990,6);
		double upperLimit = 4000.00;
		double lowerLimit = 2000.00;
		double preSettlementPrice  = 3100.00;
		Market market(0,variety,lastPrice,0,buyPrices,sellPrices,upperLimit,lowerLimit,preSettlementPrice); 
		return m_marketStorage.Save(market);
	}

	Market const& CreateMarket1()
	{
		Variety variety("rb1011",1,10);
		Margins margins;
		margins.Add(Margin("test1",0.1));
		margins.Add(Margin("test2",0.2));
		variety.SetMargins(margins);
		Charge openExchageCharge("open exchange charge",Charge::PERCENT, 0.10);
		Charge openCompanyCharge("open company charge",Charge::PER_QUANTITY, 5);
		Charge offsetExchangeCharge("offset exchange charge",Charge::PER_QUANTITY, 10);
		Charge offsetCompanyCharge("offset company charge",Charge::PERCENT, 0.10);

		Charges openCharges;
		openCharges.push_back(openExchageCharge);
		openCharges.push_back(openCompanyCharge);
		Charges offsetCharges;
		offsetCharges.push_back(offsetExchangeCharge);
		offsetCharges.push_back(offsetCompanyCharge);

		variety.SetOperationCharges(Operation::OPEN, openCharges);
		variety.SetOperationCharges(Operation::OFFSET,offsetCharges);
		double lastPrice = 3100.00;
		Prices buyPrices;
		buyPrices[0] = Price(3000,9);
		Prices sellPrices;
		sellPrices[0] = Price(2980,8);
		double upperLimit = 4000.00;
		double lowerLimit = 2000.00;
		double preSettlementPrice  = 3000.00;
		Market market(1,variety,lastPrice,0,buyPrices,sellPrices,upperLimit,lowerLimit,preSettlementPrice); 
		return m_marketStorage.Save(market);
	}
	MarketStorage m_marketStorage;
};

TEST_F(FundsTest, EnoughFreezeTest)
// void foo()
{
	Funds funds(ORGINAL_AMOUNT);
	double expectedAvaliable = ORGINAL_AMOUNT - FREEZE_AMOUNT;
	EXPECT_DOUBLE_EQ(ORGINAL_AMOUNT, funds.GetAvailable());

	bool ret = funds.Freeze(1,FREEZE_AMOUNT);
	EXPECT_EQ(true,ret);
	EXPECT_DOUBLE_EQ(expectedAvaliable, funds.GetAvailable());
}

TEST_F(FundsTest, NotEnoughFreezeTest)
// void foo()
{
	Funds funds(ORGINAL_AMOUNT);

	bool ret = funds.Freeze(1,ORGINAL_AMOUNT + 1.00);
	EXPECT_EQ(false,ret);
	EXPECT_DOUBLE_EQ(ORGINAL_AMOUNT, funds.GetAvailable());
}

TEST_F(FundsTest, RepeatFreezeTest)
// void foo()
{
	Funds funds(ORGINAL_AMOUNT);
	double expectedAvaliable = ORGINAL_AMOUNT - FREEZE_AMOUNT;

	bool ret = funds.Freeze(1,FREEZE_AMOUNT);
	ret = funds.Freeze(1,FREEZE_AMOUNT);
	EXPECT_EQ(false,ret);
	EXPECT_DOUBLE_EQ(expectedAvaliable, funds.GetAvailable());
}

TEST_F(FundsTest,UnfreezeTest)
// void foo()
{
	Funds funds(ORGINAL_AMOUNT);

	funds.Freeze(1,FREEZE_AMOUNT);
	bool ret = funds.Unfreeze(1);
	EXPECT_EQ(true,ret);
	EXPECT_DOUBLE_EQ(ORGINAL_AMOUNT, funds.GetAvailable());
	ret = funds.Unfreeze(1);
	EXPECT_EQ(false,ret);
	EXPECT_DOUBLE_EQ(ORGINAL_AMOUNT, funds.GetAvailable());
}

TEST_F(FundsTest,NotExistSerialNumberUnfreezeFailedTest)
// void foo()
{
	Funds funds(ORGINAL_AMOUNT);
	double expectedAvaliable = ORGINAL_AMOUNT - FREEZE_AMOUNT;

	funds.Freeze(1,FREEZE_AMOUNT);
	bool ret = funds.Unfreeze(2);
	EXPECT_EQ(false,ret);
	EXPECT_DOUBLE_EQ(expectedAvaliable, funds.GetAvailable());
}


TEST_F(FundsTest,WithdrawTest)
//void foo()
{
	Funds funds(ORGINAL_AMOUNT);
	double expectedAvaliable = ORGINAL_AMOUNT - OPEN_AMOUNT - CHARGE_AMOUNT;
	double expectedCharge = CHARGE_AMOUNT;
	bool ret = funds.Open(OPEN_AMOUNT,CHARGE_AMOUNT);

	EXPECT_EQ(true,ret);
	EXPECT_DOUBLE_EQ(expectedAvaliable, funds.GetAvailable());
	EXPECT_DOUBLE_EQ(expectedCharge,funds.GetCharge());

	ret = funds.Open(OPEN_AMOUNT,CHARGE_AMOUNT);
	expectedAvaliable -= OPEN_AMOUNT + CHARGE_AMOUNT;
	expectedCharge += CHARGE_AMOUNT;
	EXPECT_DOUBLE_EQ(expectedAvaliable, funds.GetAvailable());
	EXPECT_DOUBLE_EQ(expectedCharge,funds.GetCharge());
}

TEST_F(FundsTest,WithdrawNotEnoughFundsTest)
// void foo()
{
	Funds funds(ORGINAL_AMOUNT);
	double expectedAvaliable = ORGINAL_AMOUNT;
	bool ret = funds.Open(ORGINAL_AMOUNT,CHARGE_AMOUNT);

	EXPECT_EQ(false,ret);
	EXPECT_DOUBLE_EQ(expectedAvaliable, funds.GetAvailable());
	EXPECT_DOUBLE_EQ(0.00,funds.GetCharge());
}

TEST_F(FundsTest,GainTest)
//void foo()
{
	Funds funds(ORGINAL_AMOUNT);
	double expectedAvaliable = ORGINAL_AMOUNT- CHARGE_AMOUNT - CHARGE_AMOUNT;
	double expectedCharge = CHARGE_AMOUNT*2;
	double expectedProfit = PROFIT_AMOUNT;
	funds.Open(OPEN_AMOUNT,CHARGE_AMOUNT);
	funds.Gain(OPEN_AMOUNT,CHARGE_AMOUNT,PROFIT_AMOUNT);
	EXPECT_DOUBLE_EQ(expectedAvaliable, funds.GetAvailable());
	EXPECT_DOUBLE_EQ(expectedCharge,funds.GetCharge());
	EXPECT_DOUBLE_EQ(expectedProfit,funds.GetGainAmount());
}

TEST_F(FundsTest,LossTest)
//void foo()
{
	Funds funds(ORGINAL_AMOUNT);
	double expectedAvaliable = ORGINAL_AMOUNT- CHARGE_AMOUNT - CHARGE_AMOUNT - PROFIT_AMOUNT;
	double expectedCharge = CHARGE_AMOUNT*2;
	double expectedProfit = PROFIT_AMOUNT;
	funds.Open(OPEN_AMOUNT,CHARGE_AMOUNT);
	funds.Loss(OPEN_AMOUNT,CHARGE_AMOUNT,PROFIT_AMOUNT);
	EXPECT_DOUBLE_EQ(expectedAvaliable, funds.GetAvailable());
	EXPECT_DOUBLE_EQ(expectedCharge,funds.GetCharge());
	EXPECT_DOUBLE_EQ(expectedProfit,funds.GetLossAmount());
}

TEST_F(FundsTest,ProfitTest)
// void foo()
{
	Funds funds(ORGINAL_AMOUNT);
	double expectedAvaliable = ORGINAL_AMOUNT- CHARGE_AMOUNT*4 - PROFIT_AMOUNT;
	double expectedCharge = CHARGE_AMOUNT*4;
	double expectedProfit = 0.00;

	funds.Open(OPEN_AMOUNT,CHARGE_AMOUNT);
	funds.Offset(OPEN_AMOUNT,CHARGE_AMOUNT,PROFIT_AMOUNT);

	EXPECT_DOUBLE_EQ(PROFIT_AMOUNT,funds.GetProfit());

	funds.Open(OPEN_AMOUNT,CHARGE_AMOUNT);
	funds.Offset(OPEN_AMOUNT,CHARGE_AMOUNT,-PROFIT_AMOUNT);

	EXPECT_DOUBLE_EQ(expectedAvaliable, funds.GetAvailable());
	EXPECT_DOUBLE_EQ(expectedCharge,funds.GetCharge());
	EXPECT_DOUBLE_EQ(expectedProfit,funds.GetProfit());
}



TEST_F(FundsTest,FreezeDealingFundTest)
// void foo()
{
	FundsService service(m_marketStorage);
	Market const& market0 = CreateMarket0();
	Order order = {0};
	order.contract = 0;
	order.direction = Direction::BUY;
	order.operation = Operation::OPEN;
	order.price = 1000.00;
	order.quantity = 10;
	Customer customer("","","");
	const double ORGINAL_AMOUNT = 1000000.00;
	customer.SetOrginalAmount(ORGINAL_AMOUNT);
	Dealing dealing(customer,order);
	dealing.OnOrdered("SystemNo");

	double expectedFreezeAmount = market0.CalculateFreezeAmount(dealing.GetOrderPrice(),10,dealing.GetOperation());
	EXPECT_DOUBLE_EQ(expectedFreezeAmount,service.FreezeDealingFund(m_marketStorage,dealing));
	EXPECT_DOUBLE_EQ(expectedFreezeAmount, customer.GetFunds().GetFreezeAmount(dealing.GetLocalId()));

}

TEST_F(FundsTest,OnOpenDealingTest)
// void foo()
{
	FundsService service(m_marketStorage);
	Market const& market0 = CreateMarket0();
	Order order = {0};
	order.contract = 0;
	order.direction = Direction::BUY;
	order.operation = Operation::OPEN;
	order.price = 1000.00;
	order.quantity = 10;
	Customer customer("","","");
	const double ORGINAL_AMOUNT = 1000000.00;
	customer.SetOrginalAmount(ORGINAL_AMOUNT);
	Dealing dealing(customer,order);
	dealing.OnOrdered("SystemNo");

	double expectedFreezeAmount = market0.CalculateFreezeAmount(dealing.GetOrderPrice(),10,dealing.GetOperation());
	service.OnDealing(dealing);
	EXPECT_DOUBLE_EQ(expectedFreezeAmount, customer.GetFunds().GetFreezeAmount(dealing.GetLocalId()));
	EXPECT_DOUBLE_EQ(ORGINAL_AMOUNT-expectedFreezeAmount,customer.GetAvailable());

	Trade trade(dealing,990.00,1,"SystemNo");
	dealing.OnTrade(trade);

	service.OnDealing(dealing);
	
	expectedFreezeAmount = market0.CalculateFreezeAmount(dealing.GetOrderPrice(),9,dealing.GetOperation());
	
	EXPECT_DOUBLE_EQ(expectedFreezeAmount, customer.GetFunds().GetFreezeAmount(dealing.GetLocalId()));
	EXPECT_DOUBLE_EQ(ORGINAL_AMOUNT-expectedFreezeAmount,customer.GetAvailable());

}


TEST_F(FundsTest,OnTradeTest)
// void foo()
{
	FundsService service(m_marketStorage);
	Market const& market0 = CreateMarket0();
	Customer customer("","","");
	const double ORGINAL_AMOUNT = 1000000.00;
	customer.SetOrginalAmount(ORGINAL_AMOUNT);
	{
		Order order = {0};
		order.contract = 0;
		order.direction = Direction::BUY;
		order.operation = Operation::OPEN;
		order.price = 1000.00;
		order.quantity = 10;
		Dealing dealing(customer,order);
		dealing.OnOrdered("SystemNo");

		service.OnDealing(dealing);

		Trade trade(dealing,990.00,1,"SystemNo");
		dealing.OnTrade(trade);

		service.OnDealing(dealing);
		service.OnTrade(trade);

		PositionValue const& value = customer.GetPosition(PositionKey(order.contract,order.direction,Operation::OFFSET_TODAY));
		EXPECT_EQ(1,value.GetQuantity());
		EXPECT_DOUBLE_EQ(990.00,value.GetPrice());

		double expectedFreezeAmount = market0.CalculateFreezeAmount(dealing.GetOrderPrice(),9,dealing.GetOperation());
		double expectedCharge = market0.CalculateCharge(trade.GetDealPrice(),trade.GetDealQuantity(),trade.GetDealing().GetOperation());
		double openAmount = market0.CalculateMargin(dealing.GetAverageDealPrice(),1,dealing.GetOperation());
		double expectedAvailable = ORGINAL_AMOUNT - expectedFreezeAmount - openAmount - expectedCharge;
		EXPECT_DOUBLE_EQ(expectedCharge, customer.GetFunds().GetCharge());
		EXPECT_DOUBLE_EQ(expectedFreezeAmount, customer.GetFunds().GetFreezeAmount(dealing.GetLocalId()));
		EXPECT_DOUBLE_EQ(expectedAvailable,customer.GetAvailable());

		Trade trade2(dealing,1000.00,9,"SystemNo");
		dealing.OnTrade(trade2);

		service.OnDealing(dealing);
		service.OnTrade(trade2);

		EXPECT_EQ(10,value.GetQuantity());
		EXPECT_DOUBLE_EQ(999.00,value.GetPrice());

		expectedFreezeAmount = 0;
		expectedCharge += market0.CalculateCharge(trade2.GetDealPrice(),trade2.GetDealQuantity(),trade2.GetDealing().GetOperation());
		openAmount = market0.CalculateMargin(dealing.GetAverageDealPrice(),10,dealing.GetOperation());
		expectedAvailable = ORGINAL_AMOUNT - expectedFreezeAmount - openAmount - expectedCharge;
		EXPECT_DOUBLE_EQ(expectedCharge, customer.GetFunds().GetCharge());
		EXPECT_DOUBLE_EQ(expectedFreezeAmount, customer.GetFunds().GetFreezeAmount(dealing.GetLocalId()));
		EXPECT_DOUBLE_EQ(expectedAvailable,customer.GetAvailable());
	}


	{
		double oldCharge = customer.GetFunds().GetCharge();
		double oldAvailable = customer.GetFunds().GetAvailable();
		Order order = {0};
		order.contract = 0;
		order.direction = Direction::SELL;
		order.operation = Operation::OFFSET_TODAY;
		order.price = 1000.00;
		order.quantity = 10;
		Dealing dealing(customer,order);
		dealing.OnOrdered("SystemNo");

		service.OnDealing(dealing);


		Trade trade(dealing,1100.00,1,"SystemNo");
		dealing.OnTrade(trade);

		service.OnDealing(dealing);
		service.OnTrade(trade);

		PositionValue const& value = customer.GetPosition(PositionKey(order.contract,Direction::BUY,Operation::OFFSET_TODAY));
		EXPECT_EQ(9,value.GetQuantity());
		EXPECT_DOUBLE_EQ(999.00,value.GetPrice());

		double expectedFreezeAmount = 0;
		double expectedCharge = market0.CalculateCharge(trade.GetDealPrice(),trade.GetDealQuantity(),trade.GetDealing().GetOperation());
		double returnAmount = market0.CalculateMargin(999.00,trade.GetDealQuantity(),Operation::OPEN);
		double expectedAvailable = oldAvailable - expectedCharge + returnAmount;

		EXPECT_DOUBLE_EQ(expectedCharge, customer.GetFunds().GetCharge() - oldCharge);
		EXPECT_DOUBLE_EQ(expectedFreezeAmount, customer.GetFunds().GetFreezeAmount(dealing.GetLocalId()));
		EXPECT_TRUE(customer.GetAvailable() < ORGINAL_AMOUNT);
		EXPECT_DOUBLE_EQ(expectedAvailable,customer.GetAvailable());

		Trade trade2(dealing,1000.00,8,"SystemNo");
		dealing.OnTrade(trade2);

		service.OnDealing(dealing);
		service.OnTrade(trade2);

		EXPECT_EQ(1,value.GetQuantity());
		EXPECT_DOUBLE_EQ(999.00,value.GetPrice());

		expectedFreezeAmount = 0;
		double charge = market0.CalculateCharge(trade2.GetDealPrice(),trade2.GetDealQuantity(),trade2.GetDealing().GetOperation());
		expectedCharge += charge;
		returnAmount = market0.CalculateMargin(999.00,trade2.GetDealQuantity(),Operation::OPEN);
		expectedAvailable = expectedAvailable - charge + returnAmount;
		EXPECT_DOUBLE_EQ(expectedCharge, customer.GetFunds().GetCharge() - oldCharge);
		EXPECT_DOUBLE_EQ(expectedFreezeAmount, customer.GetFunds().GetFreezeAmount(dealing.GetLocalId()));
		EXPECT_TRUE(customer.GetAvailable() < ORGINAL_AMOUNT);
		EXPECT_DOUBLE_EQ(expectedAvailable,customer.GetAvailable());

		Trade trade3(dealing,1000.00,1,"SystemNo");
		dealing.OnTrade(trade3);

		service.OnDealing(dealing);
		service.OnTrade(trade3);

		EXPECT_EQ(0,value.GetQuantity());
		EXPECT_DOUBLE_EQ(0,value.GetPrice());

		expectedFreezeAmount = 0;
		charge = market0.CalculateCharge(trade3.GetDealPrice(),trade3.GetDealQuantity(),trade3.GetDealing().GetOperation());
		expectedCharge += charge;
		returnAmount = market0.CalculateMargin(999.00,trade3.GetDealQuantity(),Operation::OPEN);
		expectedAvailable = expectedAvailable - charge +  returnAmount;
		EXPECT_DOUBLE_EQ(expectedCharge, customer.GetFunds().GetCharge() - oldCharge);
		EXPECT_DOUBLE_EQ(expectedFreezeAmount, customer.GetFunds().GetFreezeAmount(dealing.GetLocalId()));
		EXPECT_TRUE(customer.GetAvailable() < ORGINAL_AMOUNT);
		EXPECT_DOUBLE_EQ(expectedAvailable,customer.GetAvailable());	
	}
}